object Test extends Application {
  trait Monoid[T] { 
    val zero: T 
    def <> (x: T, y: T): T 
  }
  
  implicit object IntPlusMonoid extends Monoid[Int] { 
    val zero = 0
    def <>(x: Int, y: Int) = x + y
  }


  def combine[T](xs: Array[T], ys: Array[T])(op: (T, T) => T)(implicit m: Monoid[T]): T = { import m._
    (xs zip ys).foldLeft(zero){(accum, xy) => <>(accum, op(xy._1, xy._2))}
  }

  val q  = Array( 1, 2, 3, 4, 5, 6)                                
  val to = Array( 10, 20, 30, 40, 50, 60)                          

  println(combine(q, to)(_ * _)) // could explicitly specify in which monoid you want this combination to occur
  
  
  implicit object DoubleTimesMonoid extends Monoid[Double] { 
    val zero = 1.0
    def <>(x: Double, y: Double) = x * y
  }

  def gen[T](f: Int => T)(range: Range): Array[T] = range.map(f(_)).toList.toArray 

  class Probability(q: Array[double], to: Array[double], t: int, x: int, TM: double) {
    def Q(i: int) = q(x+i+t)*TM*(1-to(i)/2)

    def T(i: int) = to(i)*(1-q(x+i-t)*TM/2)

    def MJ(i: int) = combine(gen(Q)(t to i), gen(T)(t to i)){ (q, t) => 1.0 - q - t}
  
    /*{
      var result: double = 1
      for (is <- t to i)
        result = result * (1.0-Q(is)-T(is))
      result
    }*/
  }  
}